package com.inspur.edp.common.expr.visitor.type;

import com.inspur.edp.common.expr.ast.CompileUnitParser;
import com.inspur.edp.common.expr.exception.ExpressException;
import com.inspur.edp.common.expr.parser.TypeAstParser;
import com.inspur.edp.common.expr.visitor.JavaVisitor;
import com.inspur.edp.common.type.Type;
import com.inspur.edp.common.type.TypeRefer;
import com.inspur.edp.common.type.utils.TypeUtil;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author lizhaorui
 * @description TODO
 * @date 2024/6/3 16:50
 */
public class TypeVisitor {

    private static TypeVisitor instance;

    private Map<Class, TypeInternalVisitor> visitMap = new HashMap<>();

    public static TypeVisitor getInstance() {
        if (instance == null) {
            synchronized (TypeVisitor.class) {
                if (instance == null) {
                    TypeVisitor temp = new TypeVisitor();
                    temp.visitMap.put(CompileUnitParser.ReferTypeContext.class, new ReferTypeVisitor());
                    temp.visitMap.put(CompileUnitParser.PrimitiveTypeContext.class, new PrimitiveTypeVisitor());
                    temp.visitMap.put(CompileUnitParser.ArrayTypeContext.class, new ArrayTypeVisitor());
                    temp.visitMap.put(CompileUnitParser.GenericTypeContext.class, new GenericTypeVisitor());
                    temp.visitMap.put(CompileUnitParser.VoidTypeContext.class, new VoidTypeVisitor());
                    instance = temp;
                }
            }
        }
        return instance;
    }

    public static String visit(CompileUnitParser.TypeContext ctx, JavaVisitor compileUnitVisitor) {
        TypeRefer dslType = TypeAstParser.getTypeRefer(ctx, compileUnitVisitor);
        return visit(dslType, compileUnitVisitor);
    }

    public static String visit(TypeRefer dslType, JavaVisitor compileUnitVisitor) {
        List<String> importList = JavaTypeReferConverter.getImports(dslType);
        compileUnitVisitor.addJavaTypeImports(importList);
        String javaClassName = JavaTypeReferConverter.getJavaClassName(dslType);
        return javaClassName;
    }

    public static TypeInternalVisitor getVisitor(CompileUnitParser.TypeContext ctx) {
        if (ctx == null) {
            throw new ExpressException("ctx is null");
        }
        Class cls = ctx.getClass();
        TypeVisitor manager = TypeVisitor.getInstance();
        if (manager.visitMap.containsKey(cls)) {
            return manager.visitMap.get(cls);
        } else {
            throw new ExpressException("not support primitive type " + cls.getName());
        }
    }

    interface TypeInternalVisitor {
        String visit(JavaVisitor visitor, CompileUnitParser.TypeContext ctx);
    }


    static class ReferTypeVisitor implements TypeInternalVisitor {
        @Override
        public String visit(JavaVisitor visitor, CompileUnitParser.TypeContext ctx) {
            String typeName = visitor.visitReferType((CompileUnitParser.ReferTypeContext) ctx).toString();
            String typeUrl = typeName;
            if (visitor.getDslImportMap().containsKey(typeName)) {
                typeUrl = visitor.getDslImportMap().get(typeName);
            }
            Type type = visitor.getType(new TypeRefer(typeUrl));
            String typeFullName = type.getTypeOptCode();
            return TypeUtil.getTypeCode(typeFullName);
        }
    }


    static class PrimitiveTypeVisitor implements TypeInternalVisitor {
        @Override
        public String visit(JavaVisitor visitor, CompileUnitParser.TypeContext ctx) {
            return visitor.visitPrimitiveType((CompileUnitParser.PrimitiveTypeContext) ctx).toString();
        }
    }

    static class ArrayTypeVisitor implements TypeInternalVisitor {
        @Override
        public String visit(JavaVisitor visitor, CompileUnitParser.TypeContext ctx) {
            return visitor.visitPrimitiveType((CompileUnitParser.PrimitiveTypeContext) ctx).toString();
        }
    }

    static class GenericTypeVisitor implements TypeInternalVisitor {
        @Override
        public String visit(JavaVisitor visitor, CompileUnitParser.TypeContext ctx) {
            return visitor.visitGenericType((CompileUnitParser.GenericTypeContext) ctx).toString();
        }
    }

    static class VoidTypeVisitor implements TypeInternalVisitor {
        @Override
        public String visit(JavaVisitor visitor, CompileUnitParser.TypeContext ctx) {
            return visitor.visitVoidType((CompileUnitParser.VoidTypeContext) ctx).toString();
        }
    }
}
